home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / JFC.bin / AbstractOptionPaneUI.java < prev    next >
Text File  |  1998-06-30  |  20KB  |  631 lines

  1. /*
  2.  * @(#)AbstractOptionPaneUI.java    1.19 98/02/05
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20.  
  21. package com.sun.java.swing.plaf.basic;
  22.  
  23. import com.sun.java.swing.*;
  24. import com.sun.java.swing.plaf.*;
  25. import com.sun.java.swing.event.*;
  26. import java.awt.*;
  27. import java.awt.event.ActionEvent;
  28. import java.awt.event.ActionListener;
  29. import java.io.Serializable;
  30.  
  31. /**
  32.  * AbstractMessagePaneUI provides a means to place an icon, message and
  33.  * buttons into a Container. The layout will look like:<p>
  34.  * <pre>
  35.  *        ------------------
  36.  *        | i | message    |
  37.  *        | c | message    |
  38.  *        | o | message    |
  39.  *        | n | message    |
  40.  *        ------------------
  41.  *        |     buttons    |
  42.  *        |________________|
  43.  * </pre>
  44.  * icon is an instance of Icon that is wraped inside a JLabel.
  45.  * The message is an opaque object and is tested for the following:
  46.  * if the message is a Component it is added to the Container, if
  47.  * it is an Icon it is wrapped inside a JLabel and added to the 
  48.  * Container otherwise it is wrapped inside a JLabel.
  49.  * <p>
  50.  * The Container, message, icon, and buttons are all determined from
  51.  * abstract methods.
  52.  * <p>
  53.  * Warning: serialized objects of this class will not be compatible with
  54.  * future swing releases.  The current serialization support is appropriate 
  55.  * for short term storage or RMI between Swing1.0 applications.  It will
  56.  * not be possible to load serialized Swing1.0 objects with future releases
  57.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  58.  * baseline for the serialized form of Swing objects.
  59.  *
  60.  * @version 1.19 02/05/98
  61.  * @author James Gosling
  62.  * @author Scott Violet
  63.  */
  64. abstract public class AbstractOptionPaneUI extends OptionPaneUI implements Serializable
  65. {
  66.     /** Component to receive focus when messaged with selectInitialValue. */
  67.     protected Component           initialFocusComponent;
  68.  
  69.     /** This is set to true in validateComponent if a Component is contained
  70.      * in either the message or the buttons. */
  71.     protected boolean             hasCustomComponents;
  72.  
  73.     /**
  74.      * Icon to display, null implies no Icon.
  75.      */
  76.     abstract public Icon getIcon();
  77.  
  78.     /**
  79.      * Returns the message to display.
  80.      */
  81.     abstract public Object getMessage();
  82.  
  83.     /** 
  84.      * Returns the buttons to display.
  85.      */
  86.     abstract public Object[] getButtons();
  87.  
  88.     /**
  89.      * Returns the Container to place all the Components in.
  90.      */
  91.     abstract public Container getContainer();
  92.  
  93.     /**
  94.      * Returns the initial index into buttons to select.
  95.      */
  96.     abstract public int getInitialIndex();
  97.  
  98.     protected Color getLabelColor() { return UIManager.getColor( "OptionPane.foreground" ); }
  99.  
  100.     /**
  101.      * Returns false. This is queried to determine if the buttons should
  102.      * be sized to the same width.
  103.      */
  104.     public boolean getSizeButtonsToSameWidth() {
  105.     return false;
  106.     }
  107.  
  108.     /**
  109.      * Returns true if in the last call to validateComponent the message
  110.      * or buttons contained a subclass of Component.
  111.      */
  112.     public boolean containsCustomComponents() {
  113.     return hasCustomComponents;
  114.     }
  115.  
  116.     /**
  117.      * Returns the maximum number of characters to place on a line.
  118.      * Default is to return Integer.MAX_VALUE. Concrete implementations
  119.      * may want to return a value that means something.
  120.      */
  121.     public int getMaxCharactersPerLineCount() {
  122.     return Integer.MAX_VALUE;
  123.     }
  124.  
  125.     /**
  126.      * Requests focus on the initial value.
  127.      */
  128.     public void selectInitialValue() {
  129.     if (initialFocusComponent != null)
  130.         initialFocusComponent.requestFocus();
  131.  
  132.         if (initialFocusComponent instanceof JButton) {
  133.             JRootPane root = SwingUtilities.getRootPane(initialFocusComponent);
  134.             if (root != null) {
  135.                 root.setDefaultButton((JButton)initialFocusComponent);
  136.             }
  137.         }
  138.     }
  139.  
  140.     /**
  141.      * Recursively creates new JLabel instances to represent <code>d</code>.
  142.      * Each JLabel instance is added to <code>c</code>.
  143.      */
  144.     protected void burstStringInto(Container c, String d, int maxll) {
  145.     // Primitive line wrapping
  146.     int len = d.length();
  147.     if (len <= 0)
  148.         return;
  149.     if (len > maxll) {
  150.         int p = d.lastIndexOf(' ', maxll);
  151.         if (p <= 0)
  152.         p = d.indexOf(' ', maxll);
  153.         if (p > 0 && p < len) {
  154.         burstStringInto(c, d.substring(0, p), maxll);
  155.         burstStringInto(c, d.substring(p + 1), maxll);
  156.         return;
  157.         }
  158.     }
  159.     c.add(new JLabel(d, JLabel.LEFT));
  160.     }
  161.  
  162.     /**
  163.      * Creates the appropriate object to represent <code>d</code> and
  164.      * places it into <code>container</code>. If <code>d</code> is an
  165.      * instance of Component, it is added directly, if it is an Icon,
  166.      * a JLabel is created to represent it, otherwise a JLabel is
  167.      * created for the string, if <code>d</code> is an Object[], this
  168.      * method will be recursively invoked for the children.
  169.      * <code>internallyCreated</code> is true if Objc is an instance
  170.      * of Component and was created internally by this method (this is
  171.      * used to correctly set hasCustomComponents only if !internallyCreated).
  172.      */
  173.     protected void appendDescription(Container container,
  174.                      GridBagConstraints cons,
  175.                      Object d, int maxll,
  176.                      boolean internallyCreated) {
  177.     if (d == null)
  178.         return;
  179.     if (d instanceof Component) {
  180.         cons.fill = GridBagConstraints.HORIZONTAL;
  181.         cons.weightx = 1;
  182.         container.add((Component) d, cons);
  183.         cons.weightx = 0;
  184.         cons.fill = GridBagConstraints.NONE;
  185.         cons.gridy++;
  186.         if(!internallyCreated)
  187.         hasCustomComponents = true;
  188.     } else if (d instanceof Object[]) {
  189.         Object [] da = (Object[]) d;
  190.         for (int i = 0; i < da.length; i++)
  191.         appendDescription(container, cons, da[i], maxll, false);
  192.     } else if (d instanceof Icon) {
  193.         JLabel label = new JLabel( (Icon)d, maxll );
  194.         label.setForeground( getLabelColor() );
  195.         appendDescription(container, cons, label, maxll, true);
  196.     } else {
  197.         String s = d.toString();
  198.         int len = s.length();
  199.         if (len <= 0)
  200.         return;
  201.         int nl = s.indexOf('\n');
  202.         if (nl >= 0) {
  203.         // break up newlines
  204.         if(nl == 0)
  205.             appendDescription(container, cons, new Component() {
  206.                 public Dimension getPreferredSize() {
  207.                 Font       f = getFont();
  208.                 
  209.                 if(f != null)
  210.                 return new Dimension(1, f.getSize() + 2);
  211.                 return new Dimension(0, 0);
  212.                 }
  213.             }, maxll, true);
  214.         else
  215.             appendDescription(container, cons, s.substring(0, nl),
  216.                       maxll, false);
  217.         appendDescription(container, cons, s.substring(nl + 1), maxll,
  218.                   false);
  219.         } else if (len > maxll) {
  220.         Container c = Box.createVerticalBox();
  221.         burstStringInto(c, s, maxll);
  222.         appendDescription(container, cons, c, maxll, true );
  223.         } else {
  224.             JLabel label = new JLabel( s, JLabel.LEFT );
  225.         label.setForeground( getLabelColor() );
  226.         appendDescription(container, cons, label, maxll, true);
  227.         }
  228.     }
  229.     }
  230.  
  231.  
  232.     /**
  233.      * Creates the appropriate object to represent each of the objects in
  234.      * <code>buttons</code> and adds it to <code>container</code>. This
  235.      * differs from appendDescription in that it will recurse on
  236.      * <code>buttons</code> and that if button is not a Component
  237.      * it will create an instance of JButton, that when pressed will
  238.      * invoke <code>createdButtonFired</code> with the appropriate
  239.      * index.
  240.      */
  241.     protected void appendButtons(Container container, Object[] buttons,
  242.                  int initialIndex) {
  243.     if(buttons != null && buttons.length > 0) {
  244.         boolean            sizeButtonsToSame = getSizeButtonsToSameWidth();
  245.         boolean            createdAll = true;
  246.         int                numButtons = buttons.length;
  247.         JButton[]          createdButtons = null;
  248.         int                maxWidth = 0;
  249.  
  250.         if(sizeButtonsToSame)
  251.         createdButtons = new JButton[numButtons];
  252.  
  253.         for(int counter = 0; counter < numButtons; counter++) {
  254.         Object       anO = buttons[counter];
  255.         Component    newComponent;
  256.  
  257.         if(anO instanceof Component) {
  258.             createdAll = false;
  259.             newComponent = (Component)anO;
  260.             container.add(newComponent);
  261.             hasCustomComponents = true;
  262.         }
  263.         else {
  264.             JButton      aButton;
  265.  
  266.             if(anO instanceof Icon)
  267.             aButton = new JButton((Icon)anO);
  268.             else
  269.             aButton = new JButton(anO.toString());
  270.             container.add(aButton);
  271.  
  272.             final int       buttonIndex = counter;
  273.  
  274.             aButton.addActionListener(new ActionListener() {
  275.             public void actionPerformed(ActionEvent e) {
  276.                 createdButtonFired(buttonIndex);
  277.             }
  278.             });
  279.             newComponent = aButton;
  280.         }
  281.         if(sizeButtonsToSame && createdAll && 
  282.            (newComponent instanceof JButton)) {
  283.             createdButtons[counter] = (JButton)newComponent;
  284.             maxWidth = Math.max(maxWidth,
  285.                     newComponent.getMinimumSize().width);
  286.         }
  287.         if(counter == initialIndex) {
  288.             initialFocusComponent = newComponent;
  289.                     if (initialFocusComponent instanceof JButton) {
  290.                         JButton defaultB = (JButton)initialFocusComponent;
  291.                         defaultB.addAncestorListener(new AncestorListener() {
  292.                            public void ancestorAdded(AncestorEvent e) { 
  293.                                JButton defaultButton = (JButton)e.getComponent();
  294.                                JRootPane root = SwingUtilities.getRootPane(defaultButton);
  295.                                if (root != null) {
  296.                                    root.setDefaultButton(defaultButton);
  297.                                }
  298.                            }
  299.                            public void ancestorRemoved(AncestorEvent event) {}
  300.                            public void ancestorMoved(AncestorEvent event) {}
  301.                         });
  302.                     }
  303.         }
  304.         }
  305.         ((SyncingLayoutManager)container.getLayout()).
  306.                       setSyncsAll((sizeButtonsToSame && createdAll));
  307.         /* Set the padding, windows seems to use 8 if <= 2 components,
  308.            otherwise 4 is used. It may actually just be the size of the
  309.            buttons is always the same, not sure. */
  310.         if(sizeButtonsToSame && createdAll) {
  311.         JButton               aButton;
  312.         int                   padSize;
  313.  
  314.         if(numButtons <= 2)
  315.             padSize = 8;
  316.         else
  317.             padSize = 4;
  318.         for(int counter = 0; counter < numButtons; counter++) {
  319.             aButton = createdButtons[counter];
  320.             aButton.setMargin(new Insets(2, padSize, 2, padSize));
  321.         }
  322.         }
  323.     }
  324.     }
  325.  
  326.     /**
  327.      * Invoked when a button that was created in <code>appendButtons</code>
  328.      * has been pressed. <code>buttonIndex</code> identifies the index
  329.      * of the button from the <code>buttons</code>array that was passed
  330.      * into <code>appendButtons</code>.
  331.      */
  332.     public void createdButtonFired(int buttonIndex) {
  333.     }
  334.  
  335.     /**
  336.      * Messaged from <code>validateComponent</code> to remove all the 
  337.      * components from <code>container</code>. This invokes removeAll
  338.      * on <code>container</code>, but is provided should subclasses wish
  339.      * to not remove everything.
  340.      */
  341.     protected void emptyContainer(Container container) {
  342.     container.removeAll();
  343.     }
  344.  
  345.     /**
  346.      * Creates and adds a JLabel representing the icon returned from
  347.      * <code>getIcon</code> to <code>top</code>. This is messaged from
  348.      * <code>createBody</code>
  349.      */
  350.     protected void addIcon(Container top) {
  351.     /* Create the icon. */
  352.     Icon                  sideIcon = getIcon();
  353.  
  354.     if (sideIcon != null) {
  355.         JLabel            iconLabel = new JLabel(sideIcon);
  356.  
  357.         iconLabel.setVerticalAlignment(SwingConstants.TOP);
  358.         top.add(iconLabel, BorderLayout.WEST);
  359.     }
  360.     }
  361.  
  362.     /**
  363.      * Messaged from validateComponent to create a Container containing the
  364.      * body of the message. The icon is the created by calling
  365.      * <code>addIcon</code>.
  366.      */
  367.     protected Container createBody() {
  368.     Container          top = new Container() {
  369.         public Insets getInsets() {
  370.         return getBodyInsets();
  371.         }
  372.     };
  373.  
  374.     top.setLayout(new BorderLayout());
  375.  
  376.     /* Fill the body. */
  377.     GridBagConstraints cons = new GridBagConstraints();
  378.     Container          body = new Container() {};
  379.     Container          realBody = new Container() {};
  380.  
  381.     realBody.setLayout(new BorderLayout());
  382.     realBody.add(new Container() {
  383.         public Dimension getPreferredSize() {
  384.         return new Dimension(15, 1);
  385.         }
  386.     }, BorderLayout.WEST);
  387.     realBody.add(body, BorderLayout.CENTER);
  388.  
  389.     body.setLayout(new GridBagLayout());
  390.     cons.gridx = cons.gridy = 0;
  391.     cons.gridwidth = GridBagConstraints.REMAINDER;
  392.     cons.gridheight = 1;
  393.     cons.anchor = GridBagConstraints.WEST;
  394.     cons.insets = new Insets(0,0,3,0);
  395.  
  396.     appendDescription(body, cons, getMessage(),
  397.               getMaxCharactersPerLineCount(), false);
  398.     top.add(realBody, BorderLayout.CENTER);
  399.  
  400.     addIcon(top);
  401.     return top;
  402.     }
  403.  
  404.     /**
  405.      * Returns the insets to be used in the Container housing the buttons.
  406.      */
  407.     protected Insets getButtonInsets() {
  408.     return new Insets(6, 0, 0, 0);
  409.     }
  410.  
  411.     /**
  412.      * Returns the insets to be used for the body, the body contains both
  413.      * the image and the actual message.
  414.      */
  415.     protected Insets getBodyInsets() {
  416.     return new Insets(0, 0, 0, 0);
  417.     }
  418.  
  419.     /**
  420.      * Creates and returns a Container containin the buttons. The buttons
  421.      * are created by calling <code>getButtons</code>.
  422.      */
  423.     protected Container createButtons() {
  424.     /* And the bottom for all the buttons. */
  425.     Container             bottom = new Container() {
  426.         public Insets getInsets() {
  427.         return getButtonInsets();
  428.         }
  429.     };
  430.  
  431.     bottom.setLayout(new SyncingLayoutManager(true, 6));
  432.     appendButtons(bottom, getButtons(), getInitialIndex());
  433.     return bottom;
  434.     }
  435.  
  436.     /**
  437.      * Removes all the components from the Container, adds the icon,
  438.      * message and buttons.
  439.      */
  440.     protected void validateComponent() {
  441.     Container        container = getContainer();
  442.  
  443.     hasCustomComponents = false;
  444.     initialFocusComponent = null;
  445.     if(container != null) {
  446.         emptyContainer(container);
  447.         container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS));
  448.         container.add(createBody());
  449.         container.add(createButtons());
  450.     }
  451.     }
  452.  
  453.  
  454.     /**
  455.      * SyncingLayoutManager acts similiar to FlowLayout. It lays out all
  456.      * components from left to right. If syncsAll is true, the widths
  457.      * of each component will be set to the largest preferred size width.
  458.      */
  459.     public static class SyncingLayoutManager implements LayoutManager,
  460.           Serializable {
  461.     protected boolean           syncsAll;
  462.     protected int               padding;
  463.         /** If true, children are lumped together in parent. */
  464.     protected boolean           centersChildren;
  465.  
  466.     public SyncingLayoutManager(boolean syncsAll, int padding) {
  467.         this.syncsAll = syncsAll;
  468.         this.padding = padding;
  469.         centersChildren = true;
  470.     }
  471.  
  472.     public void setSyncsAll(boolean newValue) {
  473.         syncsAll = newValue;
  474.     }
  475.  
  476.     public boolean getSyncsAll() {
  477.         return syncsAll;
  478.     }
  479.  
  480.     public void setPadding(int newPadding) {
  481.         this.padding = newPadding;
  482.     }
  483.  
  484.     public int getPadding() {
  485.         return padding;
  486.     }
  487.  
  488.         public void setCentersChildren(boolean newValue) {
  489.         centersChildren = newValue;
  490.     }
  491.  
  492.         public boolean getCentersChildren() {
  493.         return centersChildren;
  494.     }
  495.  
  496.     public void addLayoutComponent(String string, Component comp) {
  497.     }
  498.  
  499.     public void layoutContainer(Container container) {
  500.         Component[]      children = container.getComponents();
  501.  
  502.         if(children != null && children.length > 0) {
  503.         int               numChildren = children.length;
  504.         Dimension[]       sizes = new Dimension[numChildren];
  505.         int               counter;
  506.         int               yLocation = container.getInsets().top;
  507.  
  508.         if(syncsAll) {
  509.             int           maxWidth = 0;
  510.  
  511.             for(counter = 0; counter < numChildren; counter++) {
  512.             sizes[counter] = children[counter].getPreferredSize();
  513.             maxWidth = Math.max(maxWidth, sizes[counter].width);
  514.             }
  515.  
  516.             int      xLocation;
  517.             int      xOffset;
  518.  
  519.             if(getCentersChildren()) {
  520.             xLocation = (container.getSize().width -
  521.                       (maxWidth * numChildren +
  522.                        (numChildren - 1) * padding)) / 2;
  523.             xOffset = padding + maxWidth;
  524.             }
  525.             else {
  526.             if(numChildren > 1) {
  527.                 xLocation = 0;
  528.                 xOffset = (container.getSize().width -
  529.                        (maxWidth * numChildren)) /
  530.                 (numChildren - 1) + maxWidth;
  531.             }
  532.             else {
  533.                 xLocation = (container.getSize().width -
  534.                      maxWidth) / 2;
  535.                 xOffset = 0;
  536.             }
  537.             }
  538.             for(counter = 0; counter < numChildren; counter++) {
  539.             children[counter].setBounds(xLocation, yLocation,
  540.                             maxWidth,
  541.                             sizes[counter].height);
  542.             xLocation += xOffset;
  543.             }
  544.         }
  545.         else {
  546.             int          totalWidth = 0;
  547.  
  548.             for(counter = 0; counter < numChildren; counter++) {
  549.             sizes[counter] = children[counter].getPreferredSize();
  550.             totalWidth += sizes[counter].width;
  551.             }
  552.             totalWidth += ((numChildren - 1) * padding);
  553.  
  554.             boolean      cc = getCentersChildren();
  555.             int          xOffset;
  556.             int          xLocation;
  557.  
  558.             if(cc) {
  559.             xLocation = (container.getSize().width -
  560.                           totalWidth) / 2;
  561.             xOffset = padding;
  562.             }
  563.             else {
  564.             if(numChildren > 1) {
  565.                 xOffset = (container.getSize().width -
  566.                        totalWidth) / (numChildren - 1);    
  567.             xLocation = 0;
  568.             }
  569.             else {
  570.                 xLocation = (container.getSize().width -
  571.                      totalWidth) / 2;
  572.                 xOffset = 0;
  573.             }
  574.             }
  575.  
  576.             for(counter = 0; counter < numChildren; counter++) {
  577.             children[counter].setBounds(xLocation, yLocation,
  578.                  sizes[counter].width, sizes[counter].height);
  579.             xLocation += xOffset + sizes[counter].width;
  580.             }
  581.         }
  582.         }
  583.     }
  584.  
  585.     public Dimension minimumLayoutSize(Container c) {
  586.         if(c != null) {
  587.         Component[]       children = c.getComponents();
  588.  
  589.         if(children != null && children.length > 0) {
  590.             Dimension     aSize;
  591.             int           numChildren = children.length;
  592.             int           height = 0;
  593.             Insets        cInsets = c.getInsets();
  594.             int           extraHeight = cInsets.top + cInsets.bottom;
  595.  
  596.             if(syncsAll) {
  597.             int              maxWidth = 0;
  598.  
  599.             for(int counter = 0; counter < numChildren; counter++){
  600.                 aSize = children[counter].getPreferredSize();
  601.                 height = Math.max(height, aSize.height);
  602.                 maxWidth = Math.max(maxWidth, aSize.width);
  603.             }
  604.             return new Dimension(maxWidth * numChildren + 
  605.                          (numChildren - 1) * padding,
  606.                          extraHeight + height);
  607.             }
  608.             else {
  609.             int        totalWidth = 0;
  610.  
  611.             for(int counter = 0; counter < numChildren; counter++){
  612.                 aSize = children[counter].getPreferredSize();
  613.                 height = Math.max(height, aSize.height);
  614.                 totalWidth += aSize.width;
  615.             }
  616.             totalWidth += ((numChildren - 1) * padding);
  617.             return new Dimension(totalWidth, extraHeight + height);
  618.             }
  619.         }
  620.         }
  621.         return new Dimension(0, 0);
  622.     }
  623.  
  624.     public Dimension preferredLayoutSize(Container c) {
  625.         return minimumLayoutSize(c);
  626.     }
  627.  
  628.     public void removeLayoutComponent(Component c) { }
  629.     }
  630. }
  631.